import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
import { TIMEOUT } from "./constants";
import { RequestState, ReturnPlugin, YFWRequestOptions } from "./types";

class AxiosRequest {
  instance: AxiosInstance;
  count = 0; // 请求计数器，当取消请求时，计数器减一
  pluginsImpl: ReturnPlugin[] = [];
  state: RequestState;
  options: YFWRequestOptions;
  pluginStore: Record<string, any> = {};
  constructor(options: YFWRequestOptions) {
    this.options = options;
    this.instance = axios.create({
      timeout: TIMEOUT,
      ...options,
    });
    this.state = {
      loading: false,
      error: null,
      data: null,
      params: null,
    };
  }

  get interceptors() {
    return this.instance.interceptors;
  }

  setState(state: Partial<RequestState>) {
    this.state = {
      ...this.state,
      ...state,
    };
  }

  runPluginHandler(
    handlerName: keyof ReturnPlugin,
    ...args: any[]
  ) {
    // @ts-ignore
    const res = this.pluginsImpl.map((p) => p[handlerName]?.(...args));
    return Object.assign({}, ...res);
  }

  async request<RequestData>(params: AxiosRequestConfig): Promise<RequestData> {
    this.count += 1;
    const currentCount = this.count;
    const { returnNow, ...state } = this.runPluginHandler(
      "onBefore",
      params
    );
    this.setState({
      loading: true,
      params,
      ...state,
    });
    if (returnNow) {
      return Promise.resolve(state.data);
    }
    this.options.onBefore?.(params);
    try {
      const resultRes = await this.instance.request<AxiosRequestConfig, AxiosResponse<RequestData>>({
        ...params,
      });
      const result = resultRes.data;
      
      
      if (currentCount !== this.count) {
        return new Promise(() => {});
      }

      this.setState({
        loading: false,
        data: result,
        params,
      });

      this.options.onSuccess?.(result, params);
      this.runPluginHandler("onSuccess", result, params);
      this.options.onFinally?.(params, result, undefined);
      if (this.count === currentCount) {
        this.runPluginHandler("onFinally", params, result, undefined);
      }
      return result;
    } catch (err) {
        if (currentCount !== this.count) {
            return new Promise(() => {});
        }
        this.setState({
            loading: false,
            error: (err as Error),
          });
          this.options.onError?.(err as Error, params);
          this.runPluginHandler('onError', err, params);
    
          this.options.onFinally?.(params, undefined, err as Error);
          if (currentCount === this.count) {
            this.runPluginHandler('onFinally', params, undefined, err);
          }
          throw err;
    }
  }

  get<GetData>(url: string, params: AxiosRequestConfig = {}) {
    return this.request<GetData>({
      ...params,
      url,
      method: "GET",
    });
  }
  post<PostData>(url: string, params: AxiosRequestConfig = {}) {
    return this.request<PostData>({
      ...params,
      url,
      method: "POST",
    });
  }

  cancel() {
    this.count += 1;
    this.setState({
      loading: false,
    });
    
    this.runPluginHandler('onCancel');
  }
  refresh() {
    this.request(this.state.params);
  }
}

export default AxiosRequest;
